﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.X509;
using SealManagement.Common;
using SealManagement.Extensions;
using SealManagement.Models;
using SealManagement.SqlData;

namespace SealManagement.Controllers
{

    public class LoginController : Controller
    {
        //通过构造函数传入context上下文
        private SignContext _context;
        //private readonly ILogger _logger;
        static Logger logger = LogManager.GetCurrentClassLogger();
        private int usertype = (int)userType.User;
        private static Guid guid;
        private IMemoryCache _cache;
        private readonly IOptions<AppSettings> _options;

        //public LoginController(SignContext context,ILogger<LoginController> logger)
        public LoginController(SignContext context, IMemoryCache cache, IOptions<AppSettings> options)
        {
            _context = context;
            _cache = cache;
            //_logger = logger;
            _options = options;
        }
        /// <summary>
        /// 用户——管理员登录入口
        /// </summary>
        /// <param name="returnUrl"></param>
        /// <returns></returns>
        public IActionResult Login()
        {
            //if (!string.IsNullOrEmpty(User.Identity?.Name))
            //    return Redirect(Request.Query["ReturnUrl"].ToString() ?? "~/");
            //TempData["ReturnUrl"] = Request.Query["ReturnUrl"].ToString();
            //ViewData["ReturnUrl"] = returnUrl;
            guid = Guid.NewGuid();
            ViewData["ValidateCode"] = guid.ToString();
            HttpContext.Response.Cookies.Append("ValidateCode",
                Convert.ToBase64String(ToolClass.Encrypt(Encoding.UTF8.GetBytes(guid.ToString()))));

            var cerPath = Path.Combine(Define.Lic, _options.Value.AdminCertPath);
            byte[] certBytes = null;
            using (FileStream fsRead = new FileStream(cerPath, FileMode.Open))
            {
                int fsLen = (int)fsRead.Length;
                certBytes = new byte[fsLen];
                int r = fsRead.Read(certBytes, 0, certBytes.Length);
            }
            X509Certificate2 cert = new X509Certificate2(certBytes);
            var certParser = new Org.BouncyCastle.X509.X509CertificateParser();
            var retCert = certParser.ReadCertificate(cert.RawData);
            var alias = retCert.SubjectDN.GetValueList(X509Name.CN)[0].ToString();
            ViewData["CertStr"] = $"CN={alias},C=CN";
            ViewData["IsChecked"] = Define.IsChecked;
            return View();
        }
        /// <summary>
        /// 用户——制章人登录入口
        /// </summary>
        /// <param name="returnUrl"></param>
        /// <returns></returns>
        public IActionResult SignMakerLogin()
        {
            //ViewData["ReturnUrl"] = returnUrl;
            guid = Guid.NewGuid();
            ViewData["ValidateCode"] = guid.ToString();
            HttpContext.Response.Cookies.Append("ValidateCode",
                Convert.ToBase64String(ToolClass.Encrypt(Encoding.UTF8.GetBytes(guid.ToString()))));
            ViewData["IsChecked"] = Define.IsChecked;
            return View();
        }
        //public IActionResult SignMakerLogins(string returnUrl = null)
        //{
        //    ViewData["ReturnUrl"] = returnUrl;
        //    guid = Guid.NewGuid();
        //    ViewData["ValidateCode"] = guid.ToString();
        //    var ValidateCode = guid;
        //    HttpContext.Response.Cookies.Append("ValidateCode",
        //        Convert.ToBase64String(ToolClass.Encrypt(Encoding.UTF8.GetBytes(guid.ToString()))));
        //    return Json(new { state = resultType.Success, data = ValidateCode });
        //}
        /// <summary>
        /// 用户——管理员登录系统
        /// </summary>
        /// <param name="user">用户对象</param>
        /// <param name="returnUrl"></param>
        /// <returns></returns>
        [HttpPost]
        [EnableCors("AnyCors")]
        public async Task<IActionResult> Login(string EncryptData)
        {
            if (!Define.IsChecked)
            {
                logger.Info($"登录失败：系统未经自检，请先执行自检后操作");
                return Json(new { state = resultType.Error, message = "系统未经自检，请先执行自检后操作" });
            }
            var jsonBack = ToolClass.EncryptTransit(EncryptData);
            if (int.TryParse(jsonBack, out var errorCodeValue))
            {
                var errorCode = (ErrorCode)errorCodeValue;
                logger.Info($"登录失败：错误码：{errorCodeValue}，错误信息：{ToolClass.GetDescription(errorCode)}");
                return Json(new { state = resultType.Error, message = $"错误码：{errorCodeValue}，错误信息：{ToolClass.GetDescription(errorCode)}" });
            }
            var loginModel = JsonConvert.DeserializeObject<LoginModel>(jsonBack);
            if (loginModel == null)
            {
                logger.Info($"登录失败：数据不存在");
                return Json(new { state = resultType.Error, message = "数据不存在" });
            }

            #region 验证证书有效性
            //验证签名证书
            //证书信任链验证（只根据颁发者证书验证）
            Org.BouncyCastle.X509.X509Certificate certificate = null;
            X509CertificateParser x509CertificateParser = new X509CertificateParser();
            if (!string.IsNullOrEmpty(loginModel.SignCert))
            {
                certificate = x509CertificateParser.ReadCertificate(Convert.FromBase64String(loginModel.SignCert));
            }
            var parentPath = Path.Combine(Define.Lic, "hncasm2.cer");
            if (!System.IO.File.Exists(parentPath))
            {
                logger.Info($"登录失败：父级证书不存在");
                return Json(new { state = resultType.Error, message = "父级证书不存在" });
            }
            var certParent = x509CertificateParser.ReadCertificate(System.IO.File.ReadAllBytes(parentPath));
            try
            {
                certificate.Verify(new SM2VerifierFactoryProvider(certParent.GetPublicKey()));
            }
            catch (Exception)
            {
                logger.Info($"登录失败：证书信任链验证失败");
                return Json(new { state = resultType.Error, message = "证书信任链验证失败" });
            }
            #endregion

            #region 验证步骤
            //验证guid与cookie中的guid是否一致，验证证书是否在系统中，验证签名是否正确
            //验证发过来的明文guid是否和cookie中解密后的一致
            var guidStr = "";
            if (!HttpContext.Request.Cookies.TryGetValue("ValidateCode", out guidStr))
            {
                logger.Info($"登录失败：cookie未设置，请联系管理员");
                return Json(new { state = resultType.Error, message = "cookie未设置，请联系管理员" });
            }
            var guidFromCookie = Encoding.UTF8.GetString(ToolClass.Decrypt(Convert.FromBase64String(guidStr)));
            if (!string.Equals(guidFromCookie, loginModel.ValidateCode, StringComparison.OrdinalIgnoreCase))
            {
                logger.Info($"登录失败：cookie不正确，请刷新页面后重试");
                return Json(new { state = resultType.Error, message = "cookie不正确，请刷新页面后重试" });
            }
            //验证制章人是否存在
            if (loginModel.CertID == "")
            {
                logger.Info($"登录失败：请将信息填写完整");
                return Json(new { state = resultType.Error, message = "请将信息填写完整" });
            }
            var loginUser = _context.Users.FirstOrDefault(u => u.ArrayNo == loginModel.CertID && u.IsDelete == 0);
            if (loginUser == null)
            {
                //未获取到用户的操作
                logger.Info($"登录失败：不存在该用户，请确认信息");
                return Json(new { state = resultType.Error, message = "不存在该用户，请确认信息" });
            }
            #endregion

            //声明验证信息并将用户加入
            var claimIdentity = new ClaimsIdentity("Cookie");//用户信息加入cookie
            var uid = loginUser.Id;
            //claimIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Sid, uid.ToString()));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Name, loginUser.UserName.ToString()));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "User"));

            var claimsPrincipal = new ClaimsPrincipal(claimIdentity);

            //ViewData["ReturnUrl"] = returnUrl;
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, new AuthenticationProperties
            {
                IsPersistent = true,
                ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromMinutes(20))
            });

            var currName = claimsPrincipal.Identity.Name;
            //登录写入日志
            //logger.Info($"{User.Identity.Name}登录系统");//异步注册身份验证
            logger.Info($"登录成功：{currName}登录系统|UserID:{loginUser.ArrayNo}");
            _context.InfoLogs.Add(new InfoLog()
            {
                UserId = uid,
                UserName = currName,
                CreateDate = DateTime.Now.ToString(),
                UserType = usertype,
                Remark = $"{currName}登录系统",
                ManageType = manageType.Login.ToString()
            });
            _context.SaveChanges();
            if (!Define.IsChecked)
            {
                Define.IsChecked = true;
            }
            //跳转页面
            //returnUrl = string.IsNullOrEmpty(returnUrl) ? $"/User/Index?r={Guid.NewGuid()}" : returnUrl;
            return Json(new { state = resultType.Success, data = loginUser, returnUrl = $"/User/Index?r={Guid.NewGuid()}", IsChecked = Define.IsChecked });
            //return Json(new { state = resultType.Success, data = loginUser });
        }
        /// <summary>
        /// 用户——制章人登录系统
        /// </summary>
        /// <param name="user">用户对象</param>
        /// <param name="returnUrl"></param>
        /// <returns></returns>
        //异步编程是 ASP.NET Core 和 EF Core 的默认模式
        [HttpPost]
        public async Task<IActionResult> SignMakerLogin(string EncryptData)
        {
            if (!Define.IsChecked)
            {
                logger.Info($"登录失败：系统未经自检，请先执行自检后操作");
                return Json(new { state = resultType.Error, message = "系统未经自检，请先执行自检后操作" });
            }
            var jsonBack = ToolClass.EncryptTransit(EncryptData);
            //if (string.IsNullOrEmpty(jsonBack))
            //{
            //    return Json(new { state = resultType.Error, message = "参数错误" });
            //}
            if (int.TryParse(jsonBack, out var errorCodeValue))
            {
                var errorCode = (ErrorCode)errorCodeValue;
                logger.Info($"登录失败：错误码：{errorCodeValue}，错误信息：{ToolClass.GetDescription(errorCode)}");
                return Json(new { state = resultType.Error, message = $"错误码：{errorCodeValue}，错误信息：{ToolClass.GetDescription(errorCode)}" });
            }
            var loginModel = JsonConvert.DeserializeObject<LoginModel>(jsonBack);
            if (loginModel == null)
            {
                logger.Info($"登录失败：数据不存在");
                return Json(new { state = resultType.Error, message = "数据不存在" });
            }

            #region 验证证书有效性
            //验证签名证书
            //证书信任链验证（只根据颁发者证书验证）
            Org.BouncyCastle.X509.X509Certificate certificate = null;
            X509CertificateParser x509CertificateParser = new X509CertificateParser();
            if (!string.IsNullOrEmpty(loginModel.SignCert))
            {
                certificate = x509CertificateParser.ReadCertificate(Convert.FromBase64String(loginModel.SignCert));
            }
            var parentPath = Path.Combine(Define.Lic, "hncasm2.cer");
            if (!System.IO.File.Exists(parentPath))
            {
                logger.Info($"登录失败：父级证书不存在");
                return Json(new { state = resultType.Error, message = "父级证书不存在" });
            }
            var certParent = x509CertificateParser.ReadCertificate(System.IO.File.ReadAllBytes(parentPath));
            try
            {
                certificate.Verify(new SM2VerifierFactoryProvider(certParent.GetPublicKey()));
            }
            catch (Exception)
            {
                logger.Info($"登录失败：证书信任链验证失败");
                return Json(new { state = resultType.Error, message = "证书信任链验证失败" });
            }
            #endregion

            #region 验证步骤
            //验证guid与cookie中的guid是否一致，验证证书是否在系统中，验证签名是否正确
            //验证发过来的明文guid是否和cookie中解密后的一致
            var guidStr = "";
            if (!HttpContext.Request.Cookies.TryGetValue("ValidateCode", out guidStr))
            {
                logger.Info($"登录失败：cookie未设置，请联系管理员");
                return Json(new { state = resultType.Error, message = "cookie未设置，请联系管理员" });
            }
            //var guidToCookie = Convert.ToBase64String(ToolClass.Encrypt(Encoding.UTF8.GetBytes(ValidateCode)));
            var guidFromCookie = Encoding.UTF8.GetString(ToolClass.Decrypt(Convert.FromBase64String(guidStr)));
            //if (!string.Equals(guidToCookie, guidStr, StringComparison.OrdinalIgnoreCase))
            if (!string.Equals(guidFromCookie, loginModel.ValidateCode, StringComparison.OrdinalIgnoreCase))
            {
                logger.Info($"登录失败：cookie不正确，请刷新页面后重试");
                return Json(new { state = resultType.Error, message = "cookie不正确，请刷新页面后重试" });
            }
            //验证制章人是否存在
            if (loginModel.CertID == "")
            {
                logger.Info($"登录失败：请将信息填写完整");
                return Json(new { state = resultType.Error, message = "请将信息填写完整" });
            }
            var loginUser = _context.SignMakers.FirstOrDefault(u => u.ArrayNo == loginModel.CertID && u.IsDelete == 0);
            if (loginUser == null)
            {
                //未获取到用户的操作
                logger.Info($"登录失败：不存在该制章人，请确认信息");
                return Json(new { state = resultType.Error, message = "不存在该制章人，请确认信息" });
            }
            #endregion

            //声明验证信息并将用户加入
            var claimIdentity = new ClaimsIdentity("Cookie");//用户信息加入cookie
            var uid = loginUser.Id;
            //claimIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Sid, uid.ToString()));
            //claimIdentity.AddClaim(new Claim(ClaimTypes.Name, user.UserName.ToString()));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Name, loginUser.UserName.ToString()));
            claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "SignMaker"));

            var claimsPrincipal = new ClaimsPrincipal(claimIdentity);

            //ViewData["ReturnUrl"] = returnUrl;
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, new AuthenticationProperties
            {
                IsPersistent = true,
                ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromHours(2))
            });
            //设置登录人类型
            usertype = (int)userType.SignMaker;

            var currName = claimsPrincipal.Identity.Name;
            //登录写入日志
            //logger.Info($"{User.Identity.Name}登录系统");//异步注册身份验证
            //logger.Info($"{currName}登录系统");
            logger.Info($"登录成功：{currName}登录系统|UserID:{loginUser.ArrayNo}");
            _context.InfoLogs.Add(new InfoLog()
            {
                UserId = uid,
                UserName = currName,
                CreateDate = DateTime.Now.ToString(),
                UserType = (int)usertype,
                Remark = $"{currName}登录系统",
                ManageType = manageType.Login.ToString()
            });
            _context.SaveChanges();
            if (!Define.IsChecked)
            {
                Define.IsChecked = true;
            }
            //跳转到制章页面
            //returnUrl = string.IsNullOrEmpty(returnUrl) ? $"/ProduceSign/Create?r={Guid.NewGuid()}" : returnUrl;
            //return Redirect(returnUrl);
            //return Json(new { state = resultType.Success });
            return Json(new { state = resultType.Success, data = loginUser, returnUrl = $"/ProduceSign/Create?r={Guid.NewGuid()}", IsChecked = Define.IsChecked });
        }

        // 前后端分离需要的代码
        //public async Task<IActionResult> SignMakerLogines(string ArrayNo, string ValidateCode, string returnUrl = null)
        //{
        //    #region 验证步骤
        //    //验证guid与cookie中的guid是否一致，验证证书是否在系统中，验证签名是否正确
        //    //验证发过来的明文guid是否和cookie中解密后的一致
        //    var guidStr = "";
        //    if (!HttpContext.Request.Cookies.TryGetValue("ValidateCode", out guidStr))
        //    {
        //        return Json(new { state = resultType.Error, message = "cookie未设置，请联系管理员" });
        //    }
        //    //var guidToCookie = Convert.ToBase64String(ToolClass.Encrypt(Encoding.UTF8.GetBytes(ValidateCode)));
        //    var guidFromCookie = Encoding.UTF8.GetString(ToolClass.Decrypt(Convert.FromBase64String(guidStr)));
        //    //if (!string.Equals(guidToCookie, guidStr, StringComparison.OrdinalIgnoreCase))
        //    if (!string.Equals(guidFromCookie, ValidateCode, StringComparison.OrdinalIgnoreCase))
        //    {
        //        return Json(new { state = resultType.Error, message = "cookie不正确，请刷新页面后重试" });
        //    }
        //    //验证制章人是否存在
        //    if (ArrayNo == "")
        //        return Json(new { state = resultType.Error, message = "请将信息填写完整" });
        //    var loginUser = _context.SignMakers.FirstOrDefault(u => u.ArrayNo == ArrayNo && u.IsDelete == 0);
        //    if (loginUser == null)
        //    {
        //        //未获取到用户的操作
        //        return Json(new { state = resultType.Error, message = "不存在该制章人，请确认信息" });
        //    }
        //    //验证数字签名是否存在
        //    var publicKey = "";
        //    var hash1 = "";
        //    var digitalSign = "";
        //    //解密过程
        //    var hash2 = "";
        //    if (hash1 != hash2)
        //    {
        //        return Json(new { state = resultType.Error, message = "数字签名验证不通过，请确认信息" });
        //    }
        //    #endregion

        //    //声明验证信息并将用户加入
        //    var claimIdentity = new ClaimsIdentity("Cookie");//用户信息加入cookie
        //    var uid = loginUser.Id;
        //    //claimIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
        //    claimIdentity.AddClaim(new Claim(ClaimTypes.Sid, uid.ToString()));
        //    //claimIdentity.AddClaim(new Claim(ClaimTypes.Name, user.UserName.ToString()));
        //    claimIdentity.AddClaim(new Claim(ClaimTypes.Name, loginUser.UserName.ToString()));
        //    claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "SignMaker"));

        //    var claimsPrincipal = new ClaimsPrincipal(claimIdentity);

        //    ViewData["ReturnUrl"] = returnUrl;
        //    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, new AuthenticationProperties
        //    {
        //        IsPersistent = true,
        //        ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromHours(2))
        //    });
        //    //设置登录人类型
        //    usertype = (int)userType.SignMaker;

        //    var currName = claimsPrincipal.Identity.Name;
        //    //登录写入日志
        //    //logger.Info($"{User.Identity.Name}登录系统");//异步注册身份验证
        //    //logger.Info($"{currName}登录系统");
        //    logger.Info($"{currName}登录系统");
        //    _context.InfoLogs.Add(new InfoLog()
        //    {
        //        UserId = uid,
        //        UserName = currName,
        //        CreateDate = DateTime.Now.ToString(),
        //        UserType = (int)usertype,
        //        Remark = $"{currName}登录系统",
        //        ManageType = manageType.Login.ToString()
        //    });
        //    _context.SaveChanges();
        //    //跳转到制章页面
        //    returnUrl = string.IsNullOrEmpty(returnUrl) ? $"/ProduceSign/Create?r={Guid.NewGuid()}" : returnUrl;
        //    return Json(new { state = resultType.Success, message = "登录成功" });
        //}
        /// <summary>
        /// 用户退出系统
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> LogOut()
        {
            var uid = User.FindFirst(ClaimTypes.Sid).Value;
            var utype = Convert.ToInt32(User.Claims.First().Value);
            if (utype == (int)userType.SignMaker)
            {
                usertype = 1;
            }
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

            //退出写入日志
            //_logger.LogInformation($"{user.Id}登录系统 时间：{DateTime.Now.ToString()}");
            logger.Info($"{User.Identity.Name}退出系统");
            _context.InfoLogs.Add(new InfoLog()
            {
                UserId = Convert.ToInt32(uid),
                UserName = User.Identity.Name,
                CreateDate = DateTime.Now.ToString(),
                UserType = (int)usertype,
                Remark = $"{User.Identity.Name}退出系统",
                ManageType = manageType.Logout.ToString()
            });
            _context.SaveChanges();
            if (_cache.Get("MenuItems") != null)
                _cache.Remove("MenuItems");
            //跳转页面
            return Json(new { state = resultType.Success, message = "成功退出", logintype = usertype.ToString() });
        }

        public IActionResult Denied()
        {
            return View();
        }

        /// <summary>
        /// 自检
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> SystemCheck()
        {
            //Org.BouncyCastle.X509.X509Certificate certificate = null;
            //X509CertificateParser x509CertificateParser = new X509CertificateParser();
            //byte[] certBytes = null;
            //try
            //{
            //    certBytes = System.IO.File.ReadAllBytes(Path.Combine(Define.Lic, "sm2.cer"));
            //}
            //catch (Exception)
            //{
            //    return Json(new { state = resultType.Error, message = "证书错误" });
            //}
            //certificate = x509CertificateParser.ReadCertificate(certBytes);
            //var parentPath = Path.Combine(Define.Lic, _options.Value.ParentCertPosition);
            //if (!System.IO.File.Exists(parentPath))
            //{
            //    return Json(new { state = resultType.Error, message = "父级证书不存在" });
            //}
            //var certParent = x509CertificateParser.ReadCertificate(System.IO.File.ReadAllBytes(parentPath));
            //try
            //{
            //    certificate.Verify(new SM2VerifierFactoryProvider(certParent.GetPublicKey()));
            //}
            //catch (Exception)
            //{
            //    return Json(new { state = resultType.Error, message = "证书信任链验证失败" });
            //}
            ////验证证书中是否存在签名
            //if (!certificate.GetKeyUsage()[0])
            //{
            //    return Json(new { state = resultType.Error, message = "证书没有包含签名" });
            //}
            var systemChecked = 0;
            #region 调库自检
            lock (GMSealDll.Lock)
            {
                systemChecked = GMSealDll.SGD_SelfCheck();
            }
            #endregion
            if (systemChecked != 0)
            {
                //加入日志
                logger.Error($"自检异常，错误码：{systemChecked}");
                var errorCode = (ErrorCode)systemChecked;
                return Json(new { state = resultType.Error, message = $"错误码：{systemChecked}，错误信息：{ToolClass.GetDescription(errorCode)}" });
            }

            Define.IsChecked = true;
            logger.Info($"自检成功");
            return Json(new { state = resultType.Success, message = "自检完成", data = systemChecked });
        }

        public async Task<IActionResult> DownCert()
        {
            //if (!Define.IsChecked)
            //{
            //    return Json(new { state = resultType.Error, message = "系统未经自检，请先执行自检后操作" });
            //}
            byte[] certBytes = null;
            try
            {
                certBytes = System.IO.File.ReadAllBytes(Path.Combine(Define.Lic, "sm2.cer"));
            }
            catch (Exception)
            {
                return Json(new { state = resultType.Error, message = "证书错误" });
            }
            return File(certBytes, "text/plain", "server.cer");//文本mime
        }
    }
}